﻿using System;
using System.DirectoryServices.AccountManagement;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Red.Controls.Shell;
using Red.Launcher.Dialogs;

namespace Red.Launcher
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : RedApplication
    {
        #region Private Types

        private enum SignInResult
        {
            Success,
            Failure,
            Cancel
        }

        #endregion Private Types

        #region Private Data

        private Mutex _instanceMutex = null;

        #endregion Private Data

        #region Public Properties

        public int Changelist { get; set; }
        public int BuildId { get; set; }
        public string Version { get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); } }

        internal PrincipalContext PrincipalContext { get; private set; }
        internal UserPrincipal User { get; private set; }

        #endregion Public Properties

        #region C-Tor

        partial void InitializeAnalytics();

        public App()
        {
            Resources.MergedDictionaries.Add( new ResourceDictionary() { Source = new Uri( "pack://application:,,,/Red.Launcher;component/Menus/Workspaces/Admin/AdminDialogStructure.xaml" ) } );

            InitializeAnalytics();
        }

        #endregion C-Tor

        #region Private Methods

        private bool EnsureSingleInstance()
        {
            // check that there is only one instance of the control panel running...
            bool createdNew;

            _instanceMutex = new Mutex( true, @"RedLauncher", out createdNew );

            if ( !createdNew )
            {
                _instanceMutex = null;
                StartupUri = new Uri( "pack://application:,,,/Dialogs/MultipleInstances.xaml" );
                Current.Shutdown();
                return false;
            }

            return true;
        }

        private void ExtractVersion()
        {
            Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

            int clHigh = version.Major;
            int clLow = version.Minor;
            int bidHigh = version.Build;
            int bidLow = version.Revision;

            Changelist = ( clHigh << 16 ) | clLow;
            BuildId = ( bidHigh << 16 ) | bidLow;

            System.Diagnostics.Debug.Print( "App version: {0}", version );
            System.Diagnostics.Debug.Print( "Extracted from version Changelist: {0} BuildId: {1}", Changelist, BuildId );
        }

        private async Task<SignInResult> Signin( UserSignin signinDialog )
        {
            if ( await signinDialog.ShowAsync() )
            {
                PrincipalContext = new PrincipalContext( ContextType.Domain, signinDialog.Domain );

                // validate the credentials
                if ( PrincipalContext.ValidateCredentials( signinDialog.Username, signinDialog.Password ) )
                {
                    User = UserPrincipal.FindByIdentity( PrincipalContext, signinDialog.Username );

                    LauncherWindow mainWindow = new LauncherWindow();
                    mainWindow.Show();
                    mainWindow.Closed += MainWindow_Closed;

                    return SignInResult.Success;
                }
                else
                {
                    PrincipalContext.Dispose();
                    PrincipalContext = null;

                    // Failed to validate user
                    RedMessageBox.Show( MainWindow, RedDialogType.Ok, "Invalid logon credentials", mode: RedMessageBox.Mode.Info );
                    return SignInResult.Failure;
                }

            }
            else
            {
                // User cancelled
                Shutdown();
                return SignInResult.Cancel;
            }
        }

        #endregion Private Methods

        #region Application

        protected override void OnStartup( StartupEventArgs e )
        {
            if( Array.Exists( e.Args, arg => arg.Equals( "-signin" ) ) )
            {
                ShutdownMode = ShutdownMode.OnExplicitShutdown;
                Startup += CustomUser_Startup;
            }
            else
            {
                try
                {
                    User = UserPrincipal.Current;
                }
                catch( PrincipalServerDownException exception )
                {
                    RedMessageBox.Show( MainWindow, RedDialogType.Ok, $"Could not connect to Active Directory Service:\n\n{exception.Message}\n{exception.HResult}", "Not connected to network", RedMessageBox.Mode.Warning );
                    return;
                }
                catch( System.Runtime.InteropServices.COMException exception )
                {
                    if( exception.ErrorCode == -2147023570 )
                    {
                        RedMessageBox.Show( MainWindow, RedDialogType.Ok, $"Your password has probably just expired, and now windows is denying access to any user related data until you update your password\n\nExeception:\n{exception.Message}", "Error accessing user", RedMessageBox.Mode.Warning );
                        return;
                    }
                    else
                    {
                        throw;
                    }
                }

                StartupUri = new Uri( "pack://application:,,,/LauncherWindow.xaml" );

                if ( !EnsureSingleInstance() )
                {
                    return;
                }
            }

            ExtractVersion();

            base.OnStartup( e );
        }

        private async void CustomUser_Startup( object sender, StartupEventArgs e )
        {
            string username = null;

            // Figure out if the user specified a name on the command line
            int index = Array.IndexOf( e.Args, "-signin" ) + 1;
            if ( e.Args.Length > index && index > 0 )
            {
                if ( e.Args[ index ][ 0 ] != '-' )
                {
                    username = e.Args[ index ];
                }
            }

            SignInResult result;

            do
            {
                UserSignin signinDialog = new UserSignin() { Username = username };
                result = await Signin( signinDialog );
            }
            while ( result == SignInResult.Failure );
        }

        private void MainWindow_Closed( object sender, EventArgs e )
        {
            Shutdown();
        }

        protected override void OnExit( ExitEventArgs e )
        {
            if ( _instanceMutex != null )
                _instanceMutex.ReleaseMutex();
            base.OnExit( e );
        }

        #endregion Application
    }
}
